<template>
  <div
    class="edit-wrapper"
    ref="editWrapper"
    :style="styles"
    @click="onItemClick(id)"
    :data-component-id="id"
    @mousedown="startMove"
    :class="{ active: active, hidden: hidden }"
  >
    <slot></slot>
    <div class="resizers">
      <div class="resizer top-left" @mousedown.stop="startResize('top-left')" />
      <div
        class="resizer top-right"
        @mousedown.stop="startResize('top-right')"
      />
      <div
        class="resizer bottom-left"
        @mousedown.stop="startResize('bottom-left')"
      />
      <div
        class="resizer bottom-right"
        @mousedown.stop="startResize('bottom-right')"
      />
    </div>
  </div>
</template>

<script lang="ts">
import { defineComponent, computed, ref, nextTick } from "vue";
import { pick } from "lodash-es";
interface OriginalPositions {
  left: number;
  right: number;
  top: number;
  bottom: number;
}
type ResizeDirection =
  | "top-left"
  | "top-right"
  | "bottom-left"
  | "bottom-right";

export default defineComponent({
  name: "edit-wrapper",
  props: {
    id: {
      type: String,
      required: true,
    },
    active: {
      type: Boolean,
      default: false,
    },
    hidden: {
      type: Boolean,
      default: false,
    },
    props: {
      type: Object,
    },
  },
  emits: ["set-active", "update-position"],
  setup(props, { emit }) {
    const gap = {
      x: 0,
      y: 0,
    };
    let isMoving = false;
    const editWrapper = ref<null | HTMLElement>(null);
    const styles = computed(() =>
      pick(props.props, ["position", "top", "left", "width", "height"])
    );
    const caculateSize = (
      direction: ResizeDirection,
      e: MouseEvent,
      positions: OriginalPositions
    ) => {
      const { clientX, clientY } = e;
      const { left, right, top, bottom } = positions;
      const container = document.getElementById("canvas-area") as HTMLElement;
      const rightWidth = clientX - left;
      const leftWidth = right - clientX;
      const bottomHeight = clientY - top;
      const topHeight = bottom - clientY;
      const topOffset = clientY - container.offsetTop + container.scrollTop;
      const leftOffset = clientX - container.offsetLeft;
      switch (direction) {
        case "top-left":
          return {
            width: leftWidth,
            height: topHeight,
            top: topOffset,
            left: leftOffset,
          };
        case "top-right":
          return {
            width: rightWidth,
            height: topHeight,
            top: topOffset,
          };
        case "bottom-left":
          return {
            width: leftWidth,
            height: bottomHeight,
            left: leftOffset,
          };
        case "bottom-right":
          return {
            width: rightWidth,
            height: bottomHeight,
          };
        default:
          break;
      }
    };
    const caculateMovePosition = (e: MouseEvent) => {
      const container = document.getElementById("canvas-area") as HTMLElement;
      const left = e.clientX - gap.x - container.offsetLeft;
      const top = e.clientY - gap.y - container.offsetTop + container.scrollTop;
      return {
        left,
        top,
      };
    };
    const startMove = (e: MouseEvent) => {
      const currentElement = editWrapper.value;
      if (currentElement) {
        const { left, top } = currentElement.getBoundingClientRect();
        gap.x = e.clientX - left;
        gap.y = e.clientY - top;
      }
      const handleMove = (e: MouseEvent) => {
        const { left, top } = caculateMovePosition(e);
        isMoving = true;
        if (currentElement) {
          currentElement.style.top = `${top}px`;
          currentElement.style.left = `${left}px`;
        }
      };
      const handleMouseUp = (e: MouseEvent) => {
        document.removeEventListener("mousemove", handleMove);
        if (isMoving) {
          const { left, top } = caculateMovePosition(e);
          emit("update-position", { left, top, id: props.id });
          isMoving = false;
        }
        nextTick(() => {
          document.removeEventListener("mouseup", handleMouseUp);
        });
      };
      document.addEventListener("mousemove", handleMove);
      document.addEventListener("mouseup", handleMouseUp);
    };
    const startResize = (direction: ResizeDirection) => {
      const currentElement = editWrapper.value as HTMLElement;
      const { left, right, top, bottom } =
        currentElement.getBoundingClientRect();
      const handleMove = (e: MouseEvent) => {
        // if (currentElement) {
        //   const { left, top } = currentElement.getBoundingClientRect();
        //   currentElement.style.height = e.clientY - top + "px";
        //   currentElement.style.width = e.clientX - left + "px";
        // }
        const size = caculateSize(direction, e, { left, right, top, bottom });
        const { style } = currentElement;
        if (size) {
          style.width = size.width + "px";
          style.height = size.height + "px";
          if (size.left) {
            style.left = size.left + "px";
          }
          if (size.top) {
            style.top = size.top + "px";
          }
        }
      };
      const handleMouseUp = (e: MouseEvent) => {
        document.removeEventListener("mousemove", handleMove);
        const size = caculateSize(direction, e, { left, right, top, bottom });
        emit("update-position", { ...size, id: props.id });
        nextTick(() => {
          document.removeEventListener("mouseup", handleMouseUp);
        });
      };
      document.addEventListener("mousemove", handleMove);
      document.addEventListener("mouseup", handleMouseUp);
    };
    return {
      styles,
      editWrapper,
      startMove,
      startResize,
      onItemClick(id: string) {
        emit("set-active", id);
      },
    };
  },
});
</script>

<style lang="scss" scoped>
.edit-wrapper {
  padding: 0px;
  cursor: pointer;
  border: 1px solid transparent;
  user-select: none;
  box-sizing: content-box !important;
  :deep(*) {
    position: static !important;
    width: 100% !important;
    height: 100% !important;
  }
  .resizers {
    display: none;
  }
  &:hover {
    border: 1px dashed #ccc;
  }
  &.hidden {
    display: none;
  }
  &.active {
    border: 1px solid #1890ff;
    user-select: none;
    z-index: 1500;
    .resizers {
      display: block;
      .resizer {
        width: 10px !important;
        height: 10px !important;
        border-radius: 50%;
        background: #fff;
        border: 3px solid #1890ff;
        position: absolute !important;
        &.top-left {
          left: -5px;
          top: -5px;
          cursor: nwse-resize;
        }
        &.top-right {
          right: -5px;
          top: -5px;
          cursor: nesw-resize;
        }
        &.bottom-left {
          left: -5px;
          bottom: -5px;
          cursor: nesw-resize;
        }
        &.bottom-right {
          right: -5px;
          bottom: -5px;
          cursor: nwse-resize;
        }
      }
    }
  }
}
</style>
